/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       game_application.h

	$Header: /game/game_application.h $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "game_application.h"

#include "animation.h"
#include "bitmap_group.h"
#include "game_time.h"
#include "mouse_event.h"
#include "mouse_window.h"
#include "music.h"
#include "platform.h"
#include "sound.h"

// global variables.  All of these are local to this module.  
// these would be member variables of t_game_application, except that
// there can only be one application object.
static t_game_application*  g_application = 0;
static t_mouse_window_ptr   g_cursor;
static bool				    g_cursor_visible = true;
static t_uint32             g_last_mouse_event_time = 0;

// -------------------------------------------------------------
// default constructor for t_game_application
// -------------------------------------------------------------
t_game_application::t_game_application()
{
	g_application = this;
}

t_game_application::~t_game_application()
{
	g_application = 0;
}

t_game_application const& t_game_application::get()
{
	return *g_application;
}

// -------------------------------------------------------------
// update the cursor, if neccessary
// -------------------------------------------------------------
void t_game_application::update_cursor()
{
	t_screen_point mouse_position = get_mouse_screen_position();
	t_window*      mouse_window = t_window::get_mouse_window( mouse_position );

	update_cursor( mouse_window, mouse_position );
}

// -------------------------------------------------------------
// tell if cursor is currently visible
// -------------------------------------------------------------
bool t_game_application::is_cursor_visible()
{
	return g_cursor_visible;
}

// -------------------------------------------------------------
// set state of cursor
// -------------------------------------------------------------
void t_game_application::set_cursor_visible( bool arg  )
{
	if (arg == g_cursor_visible)
		return;
	g_cursor_visible = arg;
	if (g_cursor != 0)
	{
		g_cursor->set_visible( arg );
		return;
	}
}

// -------------------------------------------------------------
// update the cursor, if neccessary
// -------------------------------------------------------------
void t_game_application::update_cursor( t_window* window, t_screen_point const& point )
{
	// check if we need to change the cursor.

	t_mouse_window_ptr	cursor;
	t_window*			main_window = t_window::get_main_window();

	if (main_window && is_point_in_rect( point, main_window->get_screen_rect() ))
	{
		if (window == 0)
			window = t_window::get_modal_window();
	}
	else
	{
		window = 0;
	}

	if (window != 0)
		cursor = window->get_cursor();

	if (cursor == g_cursor)
	{
		if (cursor != 0)
			cursor->move_to_front();
		return;
	}

   // set the cursor bitmaps.
	if (g_cursor != 0)
	{
		g_cursor->close( false );
	}

	g_cursor = cursor;

	if (cursor == 0)
		return;

	// move the cursor to the correct location.
	cursor->open();
	cursor->set_visible( g_cursor_visible );
	cursor->move( cursor->to_parent( point ) - cursor->get_hot_spot() );

	// make it the topmost window
	cursor->move_to_front();
}

// -------------------------------------------------------------
// handle a mouse movement
// -------------------------------------------------------------
void t_game_application::mouse_move( t_window* window, t_mouse_event& event )
{
	static t_window_ptr last_mouse_window = 0;

	if (window != last_mouse_window)
	{
		// check if we need to change the cursor.
		update_cursor( window, event.screen_point );

		if (last_mouse_window != 0 && last_mouse_window->is_open())
		{
			t_mouse_event mouse_event = event;

			mouse_event.client_point = last_mouse_window->to_client( event.screen_point );
			last_mouse_window->mouse_leaving( last_mouse_window, window, mouse_event );
		}
	}


	last_mouse_window = window;
	// move the cursor.
	if (g_cursor != 0)
		g_cursor->move( g_cursor->get_parent()->to_client( event.screen_point ) 
		                - g_cursor->get_hot_spot() );

	if (window == 0)
		t_window::cancel_drag();
	else
		window->mouse_move( event );
}

// -------------------------------------------------------------
// note a mouse event by closing the help balloon and recording the time
// -------------------------------------------------------------
void t_game_application::close_help_balloon()
{
	t_window::close_help_balloon();
	g_last_mouse_event_time = get_time();
}

// -------------------------------------------------------------
// do something with the idle time
// -------------------------------------------------------------
t_uint32 t_game_application::idle()
{
	static const long	k_popup_time_ms = 1000;

	long time_till_popup = k_popup_time_ms - elapsed_time( g_last_mouse_event_time );

	if (time_till_popup < 0)
	{
		if (t_window::get_main_window() != 0 && is_cursor_visible()) {
			t_window::open_help_balloon( get_mouse_screen_position() );
		}

		// We don't need to do anything else until at least k_popup_time_ms has gone by
		time_till_popup = k_popup_time_ms;
	}

	// Reply that we don't need to be called again until it is time for a popup opportunity
	return time_till_popup;
}

// -------------------------------------------------------------
// handle a menu click
// -------------------------------------------------------------
bool t_game_application::menu_click( int id )
{
	return false;
}

// -------------------------------------------------------------
// do basic initialization
// -------------------------------------------------------------
void t_game_application::initialize()
{
}

// -------------------------------------------------------------
// run the application
// -------------------------------------------------------------
int t_game_application::run()
{
	g_last_mouse_event_time = get_time();

	initialize();
//	initialize_platform();	// This call moved inside of t_game::initialize()

	// Main message loop:
	while (true)
	{
		process_message();
	}

	return 0;
}

// -------------------------------------------------------------
// exit system
// -------------------------------------------------------------
void t_game_application::exit( int return_value )
{
	clear_music_cache();
	stop_all_sounds();


	// FIXME: if a bink window is currently playing and this call is NOT made, the game will crash
	//        because the bink window destructors are NOT being called.
	t_window::get_main_window()->close();

	if (g_cursor != 0)
	{
		g_cursor->close();
		g_cursor = 0;
	}
	t_window::close_system();
	::exit( return_value );
}


void t_game_application::create_arguments_map( std::string const& command_line )
{
	char const*	p = command_line.c_str();
	std::string key;
	std::string value;

	while (*p != 0 ) 
	{
		key = "";
		value = "";
		// skip any white space 
		while (*p == ' ' || *p == '\t' ) 
			p++;
		// gather key
		while ( *p != 0 && *p != '=' && *p != ' ' && *p != '\t')
			key += *p++;
		if ( *p == '=' ) // gather value
		{
			p++;
			while ( *p != 0 && *p != '=' && *p != ' ' && *p != '\t')
				value += *p++;
		}
		if ( key.length() > 0 )
			m_command_line_arguments.insert(std::make_pair(key,value));
	}
}
